/*
 * Copyright (c) 2005, Justin Donaldson and the haXe Project Contributors 
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

/*
 	A simple signal/slot class implemented (typically) with static functions and the Reflect API.
	These methods will NOT work for platforms that do not allow dynamic classes by default (Flash9+) for example.
	You will need to have the class implement Dynamic for Flash9+.  If you do not want to use Dynamic classes, you
	can have your trigger Class implement HaxleStatic (also not possible in Flash9+).
	
*/


package org.sugar.sigslot;


class Haxle {



private static function new(){
	
}




/* 
	Will signal all of the linked slots for the given object field with the vals array
*/


public static function signal(obj1:Dynamic, field:String, vals:Array<Dynamic>){
		if (Reflect.field(obj1,'__haxle_slots') == null) return;
		var haxle_slots:HaxleSlots = Reflect.field(obj1, '__haxle_slots');

		if (!haxle_slots.exists(field)) return;
	
		var l = haxle_slots.get(field);

		for (x in l){
			if (x.obj == null) {trace('hi');}
			var f = Reflect.field(x.obj, x.method);
			var use_vals = vals;
			if (x.mutator != null){use_vals = x.mutator(use_vals);}
			Reflect.callMethod(x.obj, f, use_vals);
		}
}


public static function signal2(obj1:Dynamic, vals:Array<Dynamic>, ?pos:haxe.PosInfos){
		var field = pos.methodName;
		trace(pos);
		return;
		if (Reflect.field(obj1,'__haxle_slots') == null) return;
		var haxle_slots:HaxleSlots = Reflect.field(obj1, '__haxle_slots');

		if (!haxle_slots.exists(field)) return;
	
		var l = haxle_slots.get(field);

		for (x in l){
			if (x.obj == null) {trace('hi');}
			var f = Reflect.field(x.obj, x.method);
			var use_vals = vals;
			if (x.mutator != null){use_vals = x.mutator(use_vals);}
			Reflect.callMethod(x.obj, f, use_vals);
		}
}

		
		
		


/* 
	Will connect an object function to another object function by way of a hidden instance field.
	The linked function must be triggered with a separate static Haxle method.  Returns true if
	the variables were connected successfully (does NOT check for type matching, etc.), false
	otherwise. 
*/

public static function connect(obj1:Dynamic, func1:String, obj2:Dynamic, func2:String, ?mutator:Array<Dynamic>->Array<Dynamic>):Bool{
	if (Reflect.field(obj1,'__haxle_slots') == null) {
		Reflect.setField(obj1,'__haxle_slots', new HaxleSlots());
	}
	var haxle_slots:HaxleSlots = Reflect.field(obj1, '__haxle_slots');

	
 	if (!haxle_slots.exists(func1)){haxle_slots.set(func1, new HaxleSlot());}
	var l = haxle_slots.get(func1);
	l.add({obj:obj2, method:func2, mutator:mutator});
	return true;
}




/* 
	Will disconnect a single instance of the given slot.  Returns true if a slot was removed.
*/
public static function disconnect(obj1:Dynamic, func1:String, obj2:Dynamic, func2:String, ?mutator:Array<Dynamic>->Array<Dynamic>):Bool{
	if (Reflect.field(obj1,'__haxle_slots') == null) return false;
	var haxle_slots:HaxleSlots = Reflect.field(obj1, '__haxle_slots');
	
	if (!haxle_slots.exists(func1)) return false;
	var l = haxle_slots.get(func1);
	var r = new HaxleSlot();
	var count:Int = 0;
	var found:Bool = false;
	for (o in l){
		if (o.obj != obj2 && mutator != null && mutator != o.mutator){r.add(o);}
		else if (count != 0){ r.add(o); count+=1;}
		else {found = true;}
	}
	haxle_slots.set(func1, r);
	return found;
}


/* 
	Will disconnect all instances of a given {slot,object,signal}, or all instances of a 
 	{slot,object}, or will reinitialize the slots (depending on which arguments are nulled).
*/
public static function disconnectAll(obj1:Dynamic, ?func1:String, ?obj2:Dynamic, ?func2:String, ?mutator:Array<Dynamic>->Array<Dynamic>){
	if (Reflect.field(obj1,'__haxle_slots') == null){ return; }
	
	var haxle_slots:HaxleSlots = Reflect.field(obj1, '__haxle_slots');
	
	// if the function wasn't specified, reinitialize the slots
	if (func1==null){
		Reflect.setField(obj1, '__haxle_slots', new HaxleSlots()); 
		return;
	}
	// if the function isn't being tracked, ignore the call
	if (!haxle_slots.exists(func1)) return;

	var l = haxle_slots.get(func1);
	var r = new HaxleSlot();
	// if obj2 doesn't exist, reinitialize the single slot
	if (obj2 != null){
		for (o in l){
			if (o.obj != obj2) r.add(o); // if the objects don't match, add it back.. or...
			else if (o.method != func2 ) r.add(o); // if the functions don't match, add it back.. or...
			else if (mutator != null && o.mutator != mutator) r.add(o); // if the mutators don't match add it back ..or...
			// don't add it at all...
		}
	}	
	haxle_slots.set(func1, r);
	
}
}

class HaxleStatic{
	public var __haxle_slots:HaxleSlots;
}


class HaxleSlots extends Hash<HaxleSlot>{
	public function new(){
		super();
	}
	
}

private class HaxleSlot extends List<{obj:Dynamic, method:String, mutator:Array<Dynamic>->Array<Dynamic>}>{
	public function new(){
		super();
	}
}



